/* A simple event-driven programming library. Originally I wrote this code
 * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
 * it in form of a library for easy reuse.
 *
 * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Redis nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#include "ae.h"
#include "zmalloc.h"
#include "config.h"

/* Include the best multiplexing layer supported by this system.
 * The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif

//创建一个事件驱动循环
//setsize表示预期当中事件的个数
aeEventLoop *aeCreateEventLoop(int setsize) {
    aeEventLoop *eventLoop;
    int i;
    //分配事件循环
    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
    //分配文件事件（I/O事件）
    eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
    //被触发的事件记录
    eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
    //分配判断
    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
    //初始化事件循环对象的配置参数
    //设置相应的预期值
    eventLoop->setsize = setsize;
    //最后响应时间
    eventLoop->lastTime = time(NULL);
    //时间事件链表
    eventLoop->timeEventHead = NULL;
    eventLoop->timeEventNextId = 0;
    eventLoop->stop = 0;
    eventLoop->maxfd = -1;

    //休眠前调用事件
    eventLoop->beforesleep = NULL;
    //休眠后调用事件
    eventLoop->aftersleep = NULL;
    //创建当前平台实际调度者
    if (aeApiCreate(eventLoop) == -1) goto err;
    /* Events with mask == AE_NONE are not set. So let's initialize the
     * vector with it. */
    //初始化预期事件数组的事件掩码
    for (i = 0; i < setsize; i++)
        eventLoop->events[i].mask = AE_NONE;
    //返回该事件循环
    return eventLoop;

    //出现错误时的出口
err:
    if (eventLoop) {
        zfree(eventLoop->events);
        zfree(eventLoop->fired);
        zfree(eventLoop);
    }
    return NULL;
}

/* Return the current set size. */
//返回当前预期事件大小
int aeGetSetSize(aeEventLoop *eventLoop) {
    return eventLoop->setsize;
}

/* Resize the maximum set size of the event loop.
 * If the requested set size is smaller than the current set size, but
 * there is already a file descriptor in use that is >= the requested
 * set size minus one, AE_ERR is returned and the operation is not
 * performed at all.
 *
 * Otherwise AE_OK is returned and the operation is successful. */
//重置当前预期事件集合的空间大小
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
    int i;
    
    //若相等则直接返回
    if (setsize == eventLoop->setsize) return AE_OK;
    //若记录中的最大描述符大于预期大小则返回错误
    if (eventLoop->maxfd >= setsize) return AE_ERR;
    //进行底层调度实际空间大小的重置，若不成功则返回错误
    if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;

    //重置上层对事件的记录空间
    eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
    //重置上层对触发事件记录空间
    eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
    //重置预期事件大小
    eventLoop->setsize = setsize;

    /* Make sure that if we created new slots, they are initialized with
     * an AE_NONE mask. */
    //设置重置大小后多出来的部分事件掩码值
    for (i = eventLoop->maxfd+1; i < setsize; i++)
        eventLoop->events[i].mask = AE_NONE;
    return AE_OK;
}

//释放事件循环
void aeDeleteEventLoop(aeEventLoop *eventLoop) {
    //释放底层结构空间
    aeApiFree(eventLoop);
    //释放注册事件空间
    zfree(eventLoop->events);
    //释放响应事件空间
    zfree(eventLoop->fired);
    //释放事件循环空间
    zfree(eventLoop);
}

//设置停止标识
void aeStop(aeEventLoop *eventLoop) {
    eventLoop->stop = 1;
}

//将描述符添加至事件循环中
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData)
{
    //若当前描述符大于预期事件值即：大于注册事件空间长度，则
    //设置errno线程错误标识符
    if (fd >= eventLoop->setsize) {
        //标识结构太大
        errno = ERANGE;
        return AE_ERR;
    }
    //取出相应描述符位置的文件事件对象
    aeFileEvent *fe = &eventLoop->events[fd];
    
    //添加至事件循环实际监听调度中
    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    //设置上层封装掩码的记录
    fe->mask |= mask;
    //同上，设置读写回调
    if (mask & AE_READABLE) fe->rfileProc = proc;
    if (mask & AE_WRITABLE) fe->wfileProc = proc;
    //设置客户端外带数据
    fe->clientData = clientData;
    //更新记录中最大的描述符值
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
}

//移除描述符事件掩码
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
{
    //若大于预期则直接返回，此时事件集合中并不存在该描述记录
    if (fd >= eventLoop->setsize) return;
    //取出相应描述符的上层封装
    aeFileEvent *fe = &eventLoop->events[fd];
    //若该描述符并未被注册监听任何事件操作则直接返回
    if (fe->mask == AE_NONE) return;

    /* We want to always remove AE_BARRIER if set when AE_WRITABLE
     * is removed. */
    //如果移除的事件包含写操作则将相应AE_BARRIER也移值
    if (mask & AE_WRITABLE) mask |= AE_BARRIER;

    //将底层的注册也移除
    aeApiDelEvent(eventLoop, fd, mask);
    //上层的注册记录也移除
    fe->mask = fe->mask & (~mask);
    //更新最大描述符记录
    //若此时描述符为最大描述符且
    // 上层记录中并没有对该描述符具有任何事件注册
    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
        /* Update the max fd */
        int j;
        //遍历更新记录的最大描述符
        for (j = eventLoop->maxfd-1; j >= 0; j--)
            if (eventLoop->events[j].mask != AE_NONE) break;
        eventLoop->maxfd = j;
    }
}
//获取相应的描述符的事件掩码
int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
    if (fd >= eventLoop->setsize) return 0;
    aeFileEvent *fe = &eventLoop->events[fd];

    return fe->mask;
}

//获取当前系统时间过的秒数与毫秒数
static void aeGetTime(long *seconds, long *milliseconds)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);
    *seconds = tv.tv_sec;
    //将微秒转换为毫秒
    *milliseconds = tv.tv_usec/1000;
}

//增加一定的毫秒数到当前时间，now() + milliseconds
//milliseconds : 延后的毫秒数
//出参：sec， ms
static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {
    long cur_sec, cur_ms, when_sec, when_ms;

    aeGetTime(&cur_sec, &cur_ms);
    when_sec = cur_sec + milliseconds/1000;
    when_ms = cur_ms + milliseconds%1000;
    if (when_ms >= 1000) {
        when_sec ++;
        when_ms -= 1000;
    }
    *sec = when_sec;
    *ms = when_ms;
}

//创建时间事件
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc)
{
    //获取最新时间事件ID
    long long id = eventLoop->timeEventNextId++;
    aeTimeEvent *te;
    //分配时间事件对象
    te = zmalloc(sizeof(*te));
    if (te == NULL) return AE_ERR;
    //设置该时间事件ID
    te->id = id;
    //设置时间事件触发时间 --> now() + milliseconds后触发
    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
    //设置时间触发时的回调
    te->timeProc = proc;
    //终结时的回调
    te->finalizerProc = finalizerProc;
    //设置外带数据
    te->clientData = clientData;
    //设置时间事件链表指向
    te->prev = NULL;
    //在事件循环从左偏头部添加
    te->next = eventLoop->timeEventHead;
    //链表操作
    if (te->next)
        te->next->prev = te;
    eventLoop->timeEventHead = te;
    //返回该时间事件ID
    return id;
}

/**
 * 删除相应时间事件
 */
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
{
    //遍历时间事件链表
    aeTimeEvent *te = eventLoop->timeEventHead;
    while(te) {
        if (te->id == id) {
            //设置为无效ID
            te->id = AE_DELETED_EVENT_ID;
            //此时链表并未发生指向性的改变
            return AE_OK;
        }
        te = te->next;
    }
    return AE_ERR; /* NO event with the specified ID found */
}

/* Search the first timer to fire.
 * This operation is useful to know how many time the select can be
 * put in sleep without to delay any event.
 * If there are no timers NULL is returned.
 *
 * Note that's O(N) since time events are unsorted.
 * Possible optimizations (not needed by Redis so far, but...):
 * 1) Insert the event in order, so that the nearest is just the head.
 *    Much better but still insertion or deletion of timers is O(N).
 * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
 */
//查询事件循环中马上要被触发的时间事件
static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
{
    aeTimeEvent *te = eventLoop->timeEventHead;
    aeTimeEvent *nearest = NULL;

    while(te) {
        if (!nearest || te->when_sec < nearest->when_sec ||
                (te->when_sec == nearest->when_sec &&
                 te->when_ms < nearest->when_ms))
            nearest = te;
        te = te->next;
    }
    return nearest;
}

/* Process time events */
//处理时间事件
static int processTimeEvents(aeEventLoop *eventLoop) {
    int processed = 0;
    aeTimeEvent *te;
    long long maxId;
    //取当前系统时间
    time_t now = time(NULL);

    /* If the system clock is moved to the future, and then set back to the
     * right value, time events may be delayed in a random way. Often this
     * means that scheduled operations will not be performed soon enough.
     *
     * Here we try to detect system clock skews, and force all the time
     * events to be processed ASAP when this happens: the idea is that
     * processing events earlier is less dangerous than delaying them
     * indefinitely, and practice suggests it is. */
    //若当前时间小于最后触发时间
    if (now < eventLoop->lastTime) {
        //此时表明所有的时间事件应该被触发
        te = eventLoop->timeEventHead;
        while(te) {
            te->when_sec = 0;
            te = te->next;
        }
    }
    //使得当前时间为最后触发时间
    eventLoop->lastTime = now;

    //获取时间事件链表头指向
    te = eventLoop->timeEventHead;
    //timeEventNextId-1表示最大时间事件的ID，详见CreateTimeEvent
    maxId = eventLoop->timeEventNextId-1;
    while(te) {
        long now_sec, now_ms;
        long long id;

        /* Remove events scheduled for deletion. */
        //若当前时间事件ID被标记为移除
        if (te->id == AE_DELETED_EVENT_ID) {
            //则进行释放
            aeTimeEvent *next = te->next;
            //进行常规链表操作
            //前节点建立连接
            if (te->prev)
                te->prev->next = te->next;
            else // 不存在前节点就表明该设置相应的事件循环中对链表头的记录
                eventLoop->timeEventHead = te->next;
            //后节点建立连接
            if (te->next)
                te->next->prev = te->prev;
            
            //调用该时间事件的终结器回调，应该用于释放客户端附加数据
            if (te->finalizerProc)
                te->finalizerProc(eventLoop, te->clientData);
            //释放该时间事件内存
            zfree(te);
            //next赋值
            te = next;
            continue;
        }

        /* Make sure we don't process time events created by time events in
         * this iteration. Note that this check is currently useless: we always
         * add new timers on the head, however if we change the implementation
         * detail, this check may be useful again: we keep it here for future
         * defense. */
        //进行边界检查，此为防御性检查
        if (te->id > maxId) {
            te = te->next;
            continue;
        }
        //获取当前系统时间
        aeGetTime(&now_sec, &now_ms);
        //若时间事件则记录的触发时刻为过去式则
        if (now_sec > te->when_sec ||
            (now_sec == te->when_sec && now_ms >= te->when_ms))
        {
            int retval;
            //进行处理回调
            id = te->id;
            retval = te->timeProc(eventLoop, id, te->clientData);
            //增加处理计数
            processed++;
            //若返回为还需要则进行重新更新下一次时间事件触发的时刻
            if (retval != AE_NOMORE) {
                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
            } else {
                //否则，将该时间事件的ID设置为`删除`标识
                te->id = AE_DELETED_EVENT_ID;
            }
        }
        //迭代
        te = te->next;
    }
    return processed;
}

/* Process every pending time event, then every pending file event
 * (that may be registered by time event callbacks just processed).
 * Without special flags the function sleeps until some file event
 * fires, or when the next time event occurs (if any).
 *
 * If flags is 0, the function does nothing and returns.
 * if flags has AE_ALL_EVENTS set, all the kind of events are processed.
 * if flags has AE_FILE_EVENTS set, file events are processed.
 * if flags has AE_TIME_EVENTS set, time events are processed.
 * if flags has AE_DONT_WAIT set the function returns ASAP until all
 * if flags has AE_CALL_AFTER_SLEEP set, the aftersleep callback is called.
 * the events that's possible to process without to wait are processed.
 *
 * The function returns the number of events processed. */
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
    int processed = 0, numevents;

    /* Nothing to do? return ASAP */
    //若执行标识既不是时间事件也不是描述符事件则直接返回，因为没事做
    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;

    /* Note that we want call select() even if there are no
     * file events to process as long as we want to process time
     * events, in order to sleep until the next time event is ready
     * to fire. */
    //若存在最大描述符或者
    //标识为时间事件且等待时则
    if (eventLoop->maxfd != -1 ||
        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
        int j;
        aeTimeEvent *shortest = NULL;
        struct timeval tv, *tvp;
        //若标识为时间事件且等待时则
        //查询最短的时间事件
        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
            shortest = aeSearchNearestTimer(eventLoop);
        //若查询时间事件不为空，此时可以确定为相应的flags标识
        if (shortest) {
            long now_sec, now_ms;
            //获取当前时间
            aeGetTime(&now_sec, &now_ms);
            //进行绑定
            tvp = &tv;

            /* How many milliseconds we need to wait for the next
             * time event to fire? */
            //获取最近时间事件与当前时间的差
            long long ms =
                (shortest->when_sec - now_sec)*1000 +
                shortest->when_ms - now_ms;
            
            //若存在时间差则进行设置
            if (ms > 0) {
                tvp->tv_sec = ms/1000;
                tvp->tv_usec = (ms % 1000)*1000;
            } else {
                //设置zero
                tvp->tv_sec = 0;
                tvp->tv_usec = 0;
            }
        } else {
            /* If we have to check for events but need to return
             * ASAP because of AE_DONT_WAIT we need to set the timeout
             * to zero */
            //若进入到此处则一定不是时间事件
            //若flags标识为不等待
            if (flags & AE_DONT_WAIT) {
                //则将相应的数值设置为zero
                tv.tv_sec = tv.tv_usec = 0;
                tvp = &tv;
            } else {
                /* Otherwise we can block */
                //若为阻止的则设置相应的指向为NULL，阻塞模式
                tvp = NULL; /* wait forever */
            }
        }

        /* Call the multiplexing API, will return only on timeout or when
         * some event fires. */
        //调用一次底层调度，通过tvp指向进行判断是否进行阻塞
        numevents = aeApiPoll(eventLoop, tvp);

        /* After sleep callback. */
        //若不存在休眠之后的回调且
        //flags标识中并设置相关标识
        if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
            eventLoop->aftersleep(eventLoop);

        //遍历响应的事件记数
        for (j = 0; j < numevents; j++) {
            //从触发事件集合中取出相应的记录信息
            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
            //得到相应的掩码
            int mask = eventLoop->fired[j].mask;
            //得到相应的描述符
            int fd = eventLoop->fired[j].fd;
            //存储当前描述符被触发的事件数量
            int fired = 0; /* Number of events fired for current fd. */

            /* Normally we execute the readable event first, and the writable
             * event laster. This is useful as sometimes we may be able
             * to serve the reply of a query immediately after processing the
             * query.
             *
             * However if AE_BARRIER is set in the mask, our application is
             * asking us to do the reverse: never fire the writable event
             * after the readable. In such a case, we invert the calls.
             * This is useful when, for instance, we want to do things
             * in the beforeSleep() hook, like fsynching a file to disk,
             * before replying to a client. */
            //通常情况下先处理读事件然后处理写事件，但存在特殊情况需要先处理写事件后再处理读
            //此时则需要依赖AE_BARRIER标识
            int invert = fe->mask & AE_BARRIER;

            /* Note the "fe->mask & mask & ..." code: maybe an already
             * processed event removed an element that fired and we still
             * didn't processed, so we check if the event is still valid.
             *
             * Fire the readable event if the call sequence is not
             * inverted. */
            //若反转后的掩码为0且
            //上层记录掩码中与响应事件掩码中都具有读事件则触发读取回调
            if (!invert && fe->mask & mask & AE_READABLE) {
                //读取回调
                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
                //更新回调记数
                fired++;
            }

            /* Fire the writable event. */
            //若上层记录掩码与响应事件掩码中都具有写事件则触发写回调
            if (fe->mask & mask & AE_WRITABLE) {
                //若触发记数为zero或者
                //写回调不为读回调则调用相应写回调
                if (!fired || fe->wfileProc != fe->rfileProc) {
                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);
                    //更新回调记数
                    fired++;
                }
            }

            /* If we have to invert the call, fire the readable event now
             * after the writable one. */
            //反转后的读事件回调
            if (invert && fe->mask & mask & AE_READABLE) {
                if (!fired || fe->wfileProc != fe->rfileProc) {
                    fe->rfileProc(eventLoop,fd,fe->clientData,mask);
                    fired++;
                }
            }
            //更新执行的响应记数
            processed++;
        }
    }
    /* Check time events */
    //若flags具有时间事件则将进行时间事件处理加上
    if (flags & AE_TIME_EVENTS)
        processed += processTimeEvents(eventLoop);

    return processed; /* return the number of processed file/time events */
}

/* Wait for milliseconds until the given file descriptor becomes
 * writable/readable/exception */
 //在描述符上进行阻塞等待，内部采用底层poll机制
int aeWait(int fd, int mask, long long milliseconds) {
    struct pollfd pfd;
    int retmask = 0, retval;

    memset(&pfd, 0, sizeof(pfd));
    //描述符
    pfd.fd = fd;
    //事件掩码
    if (mask & AE_READABLE) pfd.events |= POLLIN;
    if (mask & AE_WRITABLE) pfd.events |= POLLOUT;

    //进行阻塞等待，retval为响应结果
    if ((retval = poll(&pfd, 1, milliseconds))== 1) {
        //被触发后将触发事件转换为上层统一定义的事件掩码
        if (pfd.revents & POLLIN) retmask |= AE_READABLE;
        if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
        if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
        if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;
        return retmask;
    } else {
        //未触发
        return retval;
    }
}

//事件循环的主函数
void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    //若stop标识未被设置为true则一直循环调用
    while (!eventLoop->stop) {
        //进行阻塞休眠前的调用
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        //执行事件循环调度
        //监听时间与I/O事件，同时也执行休眠后的回调，即：
        //当从阻塞或一次底层监听中唤醒后的回调
        aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
    }
}

//获取底层实际调度的名称定义
char *aeGetApiName(void) {
    return aeApiName();
}

//设置阻塞休眠前的回调
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
    eventLoop->beforesleep = beforesleep;
}

//设置阻塞休眠后的回调
void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep) {
    eventLoop->aftersleep = aftersleep;
}
